package hn.sigit.view.workflow.transaction;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

import hn.sigit.logic.general.GeneralHelperX;
import hn.sigit.logic.general.ResourceBundleHelper;
import hn.sigit.logic.ladm.PropertyUtil;
import hn.sigit.model.commons.IParcel;
import hn.sigit.model.commons.IProperty;
import hn.sigit.model.hnd.cadastre.HND_Layer;
import hn.sigit.model.hnd.ladmshadow.Property;
import hn.sigit.model.hnd.ladmshadow.Parcel;
import hn.sigit.model.hnd.special.HND_CounterType;
import hn.sigit.dao.hnd.cadastre.HND_LayerDAO;
import hn.sigit.dao.hnd.ladmshadow.ParcelDAO;
import hn.sigit.dao.hnd.ladmshadow.PropertyDAO;
import hn.sigit.dao.hnd.special.HND_CounterDAO;


import org.apache.commons.lang.StringEscapeUtils;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.remoting.WebRemote;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTWriter;

@Name("dataEntryHelper")
@Scope(value=ScopeType.CONVERSATION)
@AutoCreate
public class DataEntryHelper implements Serializable {
	private static final long serialVersionUID = 1L;

	private TransactionHelper transactionHelper;
	
	private DataEntryRRRHelper rrrHelper;
	private DataEntrySplitHelper splitHelper;
	private DataEntryMergeHelper mergeHelper;
	private DataEntryParcelDataHelper parcelDataHelper; 
	private DataEntryUndoHelper undoHelper;
	
	private List<Property> requestedProperties;
	private List<Property> neighborProperties;
	private List<Parcel> requestedParcels;
	private List<Parcel> neighborParcels;
	
	private List<SelectItem> siRequestedParcels;

	private long presentationId;
	private long selectedLadmBaUnitId;
	private long selectedBaUnitId;
	private Double envelopeLeft;
	private Double envelopeBottom;
	private Double envelopeRight;
	private Double envelopeTop;
	
	private IParcel selectedParcel;
	private IParcel zoomedInParcel;
	private IParcel dummyParcel = Parcel.newDummyParcel();
	
	private List<HND_Layer> layerList;
	
	private List<String> presentationWkts = new ArrayList<String>();
	private ResourceBundleHelper resBundle;
	
	@In
	private GeneralHelperX generalHelper;

	
	public DataEntryHelper() {
		transactionHelper = (TransactionHelper)Component.getInstance("transactionHelper");
		presentationId = transactionHelper.getTransactionId();
		
		rrrHelper = new DataEntryRRRHelper(this);
		splitHelper = new DataEntrySplitHelper(this);
		mergeHelper = new DataEntryMergeHelper(this);
		parcelDataHelper = new DataEntryParcelDataHelper(this);
		undoHelper = new DataEntryUndoHelper(this);
		
		if (getRequestedParcels() != null && getRequestedParcels().size() > 0) {
			Parcel reqParcel = getRequestedParcels().get(0);
			setSelectedParcel(reqParcel);
		}
		
		updatePresentationWkts();
	}
	
	public long getPresentationId() {
		return presentationId;
	}
	
	public TransactionHelper getTransactionHelper() {
		return transactionHelper;
	}
	
	public DataEntryRRRHelper getRrrHelper() {
		return rrrHelper;
	}
	public DataEntrySplitHelper getSplitHelper() {
		return splitHelper;
	}
	public DataEntryMergeHelper getMergeHelper() {
		return mergeHelper;
	}
	public DataEntryParcelDataHelper getParcelDataHelper() {
		return parcelDataHelper;
	}
	public DataEntryUndoHelper getUndoHelper() {
		return undoHelper;
	}
	
	public ResourceBundleHelper getResBundle() {
		if (resBundle == null) {
			resBundle = generalHelper.getResBundle();
		}
		return resBundle;
	}

	private void updatePresentationWkts() {
		presentationWkts.clear();

		List<Property> propertyList = PropertyDAO.loadPropertiesByPresentationId(presentationId);
		WKTWriter wktWriter = new WKTWriter();
		for (Property prop : propertyList) {
			for (Polygon p : PropertyUtil.polygonsFromProperty(prop))
				presentationWkts.add(wktWriter.write(p));
		}
	}

	@WebRemote
	public String getSplitBorderWKT() {
		return splitHelper.getSplitBorderWKT();
	}
	
	@WebRemote
	public String getRequestedParcelsGML() {
		return generalHelper.getParcelsGML(this.getRequestedParcels(), 0);
	}
	
	@WebRemote
	public String getNeighborParcelsGML() {
		return generalHelper.getParcelsGML(this.getNeighborParcels(), 1);
	}

	public String getMapScript() {
		StringBuffer sb = new StringBuffer(
				"var click = new OpenLayers.Control.Click();" +
				"mapEditor.addControl(click);" +
				"click.activate();" +
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				"var select_feature_control = new OpenLayers.Control.SelectFeature(parcelFeaturesLayer,{multiple: false,toggle: true,multipleKey: 'shiftKey'});" +
				"mapEditor.addControl(select_feature_control);" +
				"select_feature_control.id = 'parcelSelectControl';"
		);
		
		for (HND_Layer layer : getLayerList())
			sb.append(generalHelper.drawLayerJS("mapEditor", layer));

		sb.append(
				"var vector_style = new OpenLayers.Style({'strokeColor': '#ff0000','strokeWidth': 3});" +
				"var vector_style_map = new OpenLayers.StyleMap({'default': vector_style});" +
				"vlayer.styleMap = vector_style_map;" +
				
				"var parcel_style = new OpenLayers.Style({" +
				"	'fillColor': '#669933'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${label}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#aaee77'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"" +
				"" +
				
				"var parcel_style_select = new OpenLayers.Style({" +
				"	'fillColor': '#3BB9FF'," +
				"	'fillOpacity': .8," +
				"	'graphicName': 'square'," +
				"	'label': '${label}'," +
				"	'pointRadius': 16," +
				"	'strokeColor': '#343434'," +
				"	'strokeDashstyle': 'solid'," +
				"	'strokeWidth': 4" +
				"});" +
				"var parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style, 'select': parcel_style_select});" +
				"var readonly_parcel_style_map = new OpenLayers.StyleMap({'default': parcel_style});" +
				
				"var parcel_symbolizer_lookup = {" +
				"	'0': {" +
				"		'fillColor': '#669933', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${label}'" +
				"	}," +
				"	'1': {" +
				"		'fillColor': '#FF0000', 'fillOpacity': .5, 'strokeColor': '#aaee77', 'strokeWidth': 3, 'pointRadius': 8, 'label': '${label}'" +
				"	}" +
				"};" +
				
				"parcel_style_map.addUniqueValueRules('default', 'neighborLevel', parcel_symbolizer_lookup);" +
				"parcelFeaturesLayer.styleMap = parcel_style_map;" +
				"neighborParcelLayer.styleMap = readonly_parcel_style_map;" +
				"" +
				"var allSZGml, spatialZoneWKT, geom;"
		);
		
		String allSZGml;
		allSZGml = generalHelper.getParcelsGML(this.getRequestedParcels(), 0);
		sb.append(mapScriptUtil(allSZGml, 0));

		allSZGml = generalHelper.getParcelsGML(this.getNeighborParcels(), 1);
		sb.append(mapScriptUtil(allSZGml, 1));
		
		for (HND_Layer layer : generalHelper.getLayerList())
			if (!layer.getFullQualifiedName().equals(generalHelper.getPrimaryWMSLayer()))
				sb.append(generalHelper.drawLayerJS("mapEditor", layer));

		return sb.toString();
	}
	private String mapScriptUtil(String gml, int neighborLevel) {
		return String.format("allSZGml = '%s';" +
				"geom = new OpenLayers.Format.GML().read(allSZGml);" +
				(neighborLevel == 0 ? "parcelFeaturesLayer" : "neighborParcelLayer") +
				".addFeatures(geom);",
				
				StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeJavaScript(gml)));
	}
				

	private List<HND_Layer> getLayerList() {
		if (layerList == null) {
			layerList = HND_LayerDAO.loadLayers(true);
		}
		return layerList;
	}
	

	public long getSelectedLadmBaUnitId() {
		return selectedLadmBaUnitId;
	}

	public long getSelectedBaUnitId() {
		return selectedBaUnitId;
	}
	private void setSelectedBaUnitId(long selectedBaUnitId) {
		if (this.selectedBaUnitId == selectedBaUnitId) return;
		
		this.selectedBaUnitId = selectedBaUnitId;
		
		//mark the RRR fields to be updated/refreshed
		if (rrrHelper != null) {
			rrrHelper.setParcelRightsList(null);
			rrrHelper.setRrrSet(null);
			rrrHelper.setProperty(null);
		}
	}
	
	
	
	public IParcel getSelectedParcel() {
		if (selectedParcel == null) return dummyParcel;
		
		return selectedParcel;
	}
	public void setSelectedParcel(IParcel selectedParcel) {
		if (this.selectedParcel == selectedParcel) return;
		
		this.selectedParcel = selectedParcel;
		
		IProperty property = selectedParcel.getProperty();
		
		setSelectedBaUnitId(property.getuID());
		selectedLadmBaUnitId = ((Property) property).getLadmId();
		
		setZoomedInParcel(selectedParcel);
	}

	public IParcel getZoomedInParcel() {
		return zoomedInParcel;
	}
	public void setZoomedInParcel(IParcel zoomedInParcel) {
		if (this.zoomedInParcel == zoomedInParcel) return;
		
		this.zoomedInParcel = zoomedInParcel;
		Polygon p = this.zoomedInParcel.getShape();
		if (p != null) {
			Polygon pEnvelope = (Polygon) p.getEnvelope();
			Coordinate[] coords = pEnvelope.getCoordinates();
			
			envelopeLeft = envelopeBottom = envelopeRight = envelopeTop = null;
			if (coords.length > 1) {
				Double minx = Double.MAX_VALUE;
				Double miny = Double.MAX_VALUE;
				Double maxx = Double.MIN_VALUE;
				Double maxy = Double.MIN_VALUE;
				for (Coordinate c : coords) {
					if (c.x < minx) minx = c.x;
					if (c.x > maxx) maxx = c.x;
					if (c.y < miny) miny = c.y;
					if (c.y > maxy) maxy = c.y;
				}
				envelopeLeft = minx;
				envelopeBottom = miny;
				envelopeRight = maxx;
				envelopeTop = maxy;
			}
		}
	}

	public Double getEnvelopeLeft() {
		return envelopeLeft;
	}

	public Double getEnvelopeBottom() {
		return envelopeBottom;
	}

	public Double getEnvelopeRight() {
		return envelopeRight;
	}

	public Double getEnvelopeTop() {
		return envelopeTop;
	}
	
	public List<Property> getRequestedProperties() {
		if (requestedProperties == null) {
			requestedProperties = PropertyDAO.loadRequestedPropertiesByPresentationId(this.presentationId);
		}
		
		return requestedProperties;
	}
	public void setRequestedProperties(List<Property> requestedProperties) {
		this.requestedProperties = requestedProperties;
	}
	
	public List<Property> getNeighborProperties() {
		if (neighborProperties == null) {
			neighborProperties = PropertyDAO.loadNeighborPropertiesByPresentationId(this.presentationId);
		}
		
		return neighborProperties;
	}
	public void setNeighborProperties(List<Property> neighborProperties) {
		this.neighborProperties = neighborProperties;
	}
	
	public List<Parcel> getRequestedParcels() {
		if (requestedParcels == null) {
			requestedParcels = ParcelDAO.loadRequestedParcelsByPresentationId(this.presentationId);
		}
		
		return requestedParcels;
	}
	public void setRequestedParcels(List<Parcel> requestedParcels) {
		this.requestedParcels = requestedParcels;
	}
	
	public List<Parcel> getNeighborParcels() {
		if (neighborParcels == null) {
			neighborParcels = ParcelDAO.loadNeighborParcelsByPresentationId(this.presentationId);
		}
		
		return neighborParcels;
	}
	public void setNeighborParcels(List<Parcel> neighborParcels) {
		this.neighborParcels = neighborParcels;
	}

	public void onPropertyClick(ActionEvent ae) {
		ParcelDAO.loadParcelById(selectedBaUnitId);
	}

	public long getFieldTabCounter() {
		return HND_CounterDAO.nextValue(HND_CounterType.FIELD_TAB);
	}

	public List<SelectItem> getSiRequestedParcels() {
		if (siRequestedParcels == null) {
			siRequestedParcels = new ArrayList<SelectItem>();
			for (Parcel p : getRequestedParcels())
				siRequestedParcels.add(new SelectItem(p, parcelText(p)));
		}
		return siRequestedParcels;
	}

	public boolean isEditing() {
		return rrrHelper.isEditing() || parcelDataHelper.isEditing();
	}
	
	public String getSelectedParcelText() {
		return parcelText(selectedParcel);
	}
	
	public String parcelText(IParcel p) {
		if (p == null) return "";
		return String.format("%s - %s",
				p.getMunicipalKey() != null ? p.getMunicipalKey() : p.getFieldTab(),
				p.getCadastralKey() != null ? p.getCadastralKey() : getResBundle().loadMessage("dataentry.no_cadastralkey"));
	}
	
	
	public String reRenderList(boolean includeTransactionCnts) {
		String defaultStr = "zoomTo,selectedParcel,panelRights,panelRestrictions,panelResponsibilities,frmEditData,splitParcelPanel,mergeParcelPanel";
		if (includeTransactionCnts)
			return "transactionCnts, " + defaultStr;
		return defaultStr;
	}
}
